#~ TYPE = "GIFT"
#~ questions_file = "loops"
#~ questions_file = "matrix"
#~ questions_file = "functions"
#~ questions_file = "variables_and_conditions"

TYPE = "CLOZE"
questions_file = "fill_missing_var_if_while_lists"
#~ questions_file = "fill_missing_2_defs_morelists_dicts_etc"
#~ questions_file = "fill_missing_tests"

questions_file = 'Questions_tpl.' + questions_file


CONVERT2CPP=False
#~ CONVERT2CPP=True

TRACE_STACK = False
MENTION_UNGENERATED_QUESTIONS = False



#~ import tokenize, trace
import inspect

from importlib import __import__
import sys
import linecache
import os.path
import sys
from io import StringIO

import helpers


call_level = 0
call_stack = []
return_stack = []
original_stdout = sys.stdout  # keep a reference to STDOUT
def start_catching_output():
    sys.stdout = StringIO()

def finish_catching_output():
        #~ out = sys.stdout.getvalue()
        sys.stdout.close()
        sys.stdout = original_stdout
        

def strip_first_line(src): return '\n'.join( src.split('\n')[1:] )

# http://docs.python.org/3/library/inspect.html
# http://pymotw.com/2/sys/tracing.html
def trace_calls(frame, event, arg):
    global call_level
    co = frame.f_code
    func_name = co.co_name
    if func_name.startswith('__')\
    or func_name in ['<listcomp>']:
        return
    if func_name in ['write', '<module>']:
        # Ignore write() calls from print statements
        return
    func_line_no = frame.f_lineno
    func_fullfilename = co.co_filename
    func_modulename = frame.f_globals["__name__"]
    #~ if event == 'call':     print("...", func_name)    
    if not func_modulename in ['__main__', questions_file]:
        return  # only trace calls in target file 

    if event == 'call':
        #~ try:    function = frame.f_globals[func_name]
        #~ except: return
        #~ print("$", co)
        #~ print("$", function)
        #~ print( ">>> ", inspect.getsourcefile(co) )
        src = inspect.getsource(co)    
        f_args, _, _, f_locals = inspect.getargvalues(frame)
        
        func_args = ','.join([repr(f_locals[arg]) for arg in f_args])
        caller = frame.f_back
        caller_line_no = caller.f_lineno
        caller_filename = caller.f_code.co_filename  # not precise...
        if call_level == 0:
            print()
            if TRACE_STACK:
                print ( '%s %s --> %s(%s) # %s' % 
                    (func_modulename, caller_line_no, func_name, func_args,
                        linecache.getline(func_fullfilename, caller_line_no) ) # caller line
                    )
            
            if TYPE == "GIFT":
                print(helpers.src_to_GIFT(
                                "%s(%s)" %( func_name, func_args ), # title
                                strip_first_line(src),          # src
                                f_args, f_locals, CONVERT2CPP
                      )
                )
            if TYPE == "CLOZE":
                global question_START
                question_START = helpers.src_to_CLOZE(
                                "%s(%s)" %( func_name, func_args ), # title
                                strip_first_line(src),          # src
                                f_args, f_locals, CONVERT2CPP,
                                pygmentize=False
                                )
                if question_START:
                    print(question_START)
                elif MENTION_UNGENERATED_QUESTIONS:
                    print("<!-- no answers generated %s(%s) -->" %( func_name, func_args ))
                    
                
            start_catching_output()
            
        call_level += 1
        call_stack.append( dict(func_name=func_name, func_args=func_args, frame=frame) )
        return trace_calls
        
    if event == 'return':
        call_level -= 1
        last_called_func = call_stack.pop()

        #~ if call_level == 0:
        #~ if call_stack == []:
        result = str(arg)
        output = sys.stdout.getvalue()
        if TYPE == "GIFT":
            output = output.replace('\n', ' ').rstrip()
            output = helpers.escape_GIFT_spec_chars(output)
        #~ replace('#', '\#').replace('=', '\=').replace('~', '\~')
        result = output + ' ' + (str(arg if arg!=None else '')        )
        
        if last_called_func['frame'] == frame:            
            return_stack.append( [func_name, "(%s)"% last_called_func['func_args'], '-->',  arg, '>>>', output] )
            
        if call_level == 0:
            finish_catching_output()

            if result.rstrip(' \n\t'):
                #~ print ("{=%s} # from %s" % (result.rstrip('\n'), func_name) )
                if TYPE == "GIFT":     
                    print ("{=%s} " % result.rstrip('\n\t ') )
                    
                if TYPE == "CLOZE":
                    if question_START:
                        print( result )  # answers are already inside question...
                        print( helpers.CLOZE_TPL_END )
                    
                        
                    
            else:
                print ("{=} \nWARNING: Empty result... NIEKO NEATSPAUSDINO")

            if TRACE_STACK:
                print ("\nRETURNS stack:")
                for x in return_stack:
                    print( x )
            del return_stack[:]
        
            
    return 

    

#TEST
sys.settrace( trace_calls )

if TYPE == "GIFT":     
    __import__( questions_file )
    
if TYPE == "CLOZE":
    print('<?xml version="1.0" encoding="UTF-8"?> <quiz>')
    __import__( questions_file )
    print('</quiz>')
